package cLog

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/sirupsen/logrus"
)

type (
	LokiHook struct {
		defaultFormater

		Host         string `json:"host"`
		Uri          string `json:"uri"`
		QueueMaxSize int    `json:"queue_max_size"`
		QueueSpeed   int    `json:"queue_speed"`
		Timeout      int64  `json:"timeout"`
		Interval     int64  `json:"interval"`

		queue []*logrus.Entry
	}

	LokiReq struct {
		Streams []*LokiReq_Stream `json:"streams"`
	}

	LokiReq_Stream struct {
		Stream map[string]string `json:"stream"`
		Values [][2]string       `json:"values"`
	}
)

func (e LokiHook) Levels() []logrus.Level {
	return logrus.AllLevels
}

func (e LokiHook) Fire(entry *logrus.Entry) (err error) {
	newEntry := entry.Dup()
	newEntry.Level = entry.Level
	newEntry.Message = entry.Message

	// 超过长度则丢弃日志
	if e.QueueMaxSize == 0 || len(e.queue) < e.QueueMaxSize {
		e.queue = append(e.queue, newEntry)
	} else {
		e.queue = e.queue[1:]
		e.queue = append(e.queue, newEntry)
	}

	return
}

func (e LokiHook) Consume() {
	e.queue = make([]*logrus.Entry, 0)
	if e.QueueSpeed <= 0 {
		e.QueueSpeed = 10
	}
	if e.QueueMaxSize <= 0 {
		e.QueueMaxSize = 0
	}
	if e.Timeout <= 0 {
		e.Timeout = 5
	}
	if e.Interval <= 0 {
		e.Interval = 1
	}

	// 启动消费协程
	go func(hook LokiHook) {
		var start int64
		for {
			total := len(hook.queue)
			if total == 0 {
				time.Sleep(time.Duration(hook.Interval) * time.Second)
				continue
			}
			if start == 0 {
				start = time.Now().Unix()
			}
			now := time.Now().Unix()
			if total < e.QueueSpeed && now-start < hook.Interval {
				continue
			}

			length := e.QueueSpeed
			if total < e.QueueSpeed {
				length = total
			}

			data := hook.queue[0:length]
			hook.queue = hook.queue[length:]

			var lokiReq = &LokiReq{Streams: []*LokiReq_Stream{}}
			for _, item := range data {
				mappingItem := toMapping(item)
				nanosec := time.Now().UnixNano()
				if t, ok := mappingItem[LOG_FIELD_DATETIME]; ok {
					var tt time.Time
					location, _ := time.LoadLocation("Asia/Shanghai")
					tt, _ = time.ParseInLocation("2006-01-02 15:04:05", t.(string), location)
					nanosec = tt.UnixNano()
				}

				outItem := map[string]any{
					LOG_FIELD_MESSAGE: mappingItem[LOG_FIELD_MESSAGE],
					LOG_FIELD_PARAMS:  mappingItem[LOG_FIELD_PARAMS],
				}

				out, _ := json.Marshal(outItem)

				var labels = make(map[string]string)
				for k, v := range mappingItem {
					if k == LOG_FIELD_PARAMS {
						continue
					}
					k = strings.ReplaceAll(k, "-", "_")
					labels[k] = fmt.Sprintf("%v", v)
					if labels[k] == "" {
						labels[k] = "null"
					}
				}

				reqItem := &LokiReq_Stream{
					Stream: labels,
					Values: [][2]string{
						{fmt.Sprintf("%d", nanosec), string(out)},
					},
				}
				lokiReq.Streams = append(lokiReq.Streams, reqItem)
			}
			lokiReqBodyByte, err := json.Marshal(lokiReq)
			if err != nil {
				continue
			}

			// 请求 loki
			url := fmt.Sprintf("%s%s", hook.Host, hook.Uri)
			client := &http.Client{
				Timeout: time.Duration(hook.Timeout) * time.Second,
			}
			req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(string(lokiReqBodyByte)))
			if err != nil {
				continue
			}
			req.Header.Set("Content-Type", "application/json")
			_, _ = client.Do(req)
		}
	}(e)
}
